package com.iamdigger.queldorei.registry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 服务地址同步器,用于更新客户端的服务地址池.
 * <p/>
 * Created by Sam on 4/7/16.
 *
 * @author Sam Tsai
 * @since 0.1.0
 */
public abstract class ServiceAddressSynchronizer {

    private final static Logger logger = LoggerFactory.getLogger(ServiceAddressSynchronizer.class);

    /**
     * 默认并发系数
     */
    private static final int DEFAULT_CONCURRENT_LEVEL = 16;

    /**
     * 默认服务数量
     */
    private static final int DEFAULT_SERVICE_COUNT = 16;


    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    private final static ReentrantLock modifyLock = new ReentrantLock();
    ;

    /**
     * 客户端本地保存的服务地址备份，使用{@link ConcurrentHashMap} 来减少不同服务竞争锁的耗时.
     */
    protected Map<String, List<String>> discoveredServices;

    public ServiceAddressSynchronizer() {
        this(0, 0);
    }

    /**
     * 服务数量以及并发等级可以试运行压力合理配置.
     *
     * @param serviceCount    预估服务数量
     * @param concurrentLevel 并发等级.合理调整可以大大减少不同服务之间竞争耗时.
     */
    public ServiceAddressSynchronizer(int serviceCount, int concurrentLevel) {
        if (serviceCount <= 0) {
            if (concurrentLevel <= 0) {
                // use all the default value
                this.discoveredServices =
                        new ConcurrentHashMap<String, List<String>>(
                                DEFAULT_SERVICE_COUNT, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENT_LEVEL
                        );
            } else {
                this.discoveredServices =
                        new ConcurrentHashMap<String, List<String>>(
                                DEFAULT_SERVICE_COUNT, DEFAULT_LOAD_FACTOR, concurrentLevel
                        );
            }
        } else {
            if (concurrentLevel <= 0)
                this.discoveredServices =
                        new ConcurrentHashMap<String, List<String>>(
                                serviceCount, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENT_LEVEL);
            else
                this.discoveredServices =
                        new ConcurrentHashMap<String, List<String>>(serviceCount, DEFAULT_LOAD_FACTOR, concurrentLevel);

        }
    }

//    public void appendServiceAddress(String serviceName, List<String> addresses) {
//        List<String> srvAddressGrp = discoveredServices.get(serviceName);
//        if (null == srvAddressGrp) {
//            srvAddressGrp = Collections.synchronizedList(new LinkedList<String>());
//        }
//        if (null != addresses && addresses.size() > 0) {
//            srvAddressGrp.addAll(addresses);
//        }
//    }

    public List<String> findServiceAddressGrp(String serviceName) {
        return discoveredServices.get(serviceName);
    }

    /**
     * 更新单个服务地址池
     *
     * @param serviceName 服务名
     * @param address     服务地址列表
     * @return Previous address list
     */
    public List<String> refreshServiceAddress(String serviceName, List<String> address) {
        if(logger.isInfoEnabled()) {
            logger.info("Service [{}] address is updated to {}", serviceName, address);
        }
        return discoveredServices.put(serviceName, address);
    }

    /**
     * 更新服务列表。入参subscribeServiceNames是全量最新的服务列表。更新的逻辑是：有则不变，
     * 没有则加，多出来的更新服务地址为空List。
     *
     * @param subscribeServiceNames 从注册中心拿来的服务列表。
     */
    public void refreshServiceName(List<String> subscribeServiceNames) {
        for (String serviceName : subscribeServiceNames) {
            if (discoveredServices.keySet().contains(serviceName)) {
                continue;
            }
            discoveredServices.put(serviceName, new ArrayList<String>());
            if(logger.isInfoEnabled()) {
                logger.info("New service synchronized. [{}]", serviceName);
            }
        }
        for (String discoveredServiceName : discoveredServices.keySet()) {
            if (!subscribeServiceNames.contains(discoveredServiceName)) {
                discoveredServices.put(discoveredServiceName, new ArrayList<String>());
                if(logger.isInfoEnabled()) {
                    logger.info("Service unregistered. [{}]", discoveredServiceName);
                }
            }
        }
    }

    /**
     * only used for modify exception
     */
    public abstract void restoreServiceAddress();

    /**
     * 同步服务
     */
    public abstract void syncServiceAddress();

    /**
     * 客户端Context启动时读取服务地址
     */
    public abstract void syncServiceAddressAtStartup();

}
